<template>
  <el-dialog v-model="props.viseble" title="上传文件" width="800" @close="close">
    <el-upload
      class="upload-demo"
      drag
      multiple
      v-model:file-list="FileList"
      :before-upload="beforeAvatarUpload"
      :auto-upload="false"
      ref="uploadRef"
      :show-file-list="false"
      destroy-on-close
    >
      <el-icon class="el-icon--upload"><upload-filled /></el-icon>
      <div class="el-upload__text">拖拽到此处 <em>点击上传</em></div>
      <template #tip>
        <div class="el-upload__tip">文件大小限制10M</div>
      </template>
    </el-upload>
    <div
      v-for="(item, index) in FileList"
      :key="index"
      style="margin-top: 10px"
      class="file-progress"
    >
      <div class="flex" style="justify-content: space-between">
        <span>{{ item.name }}</span>
        <div class="buttoms">
          <el-button v-if="item.iscommonUpload" circle size="small">
            <icon :iconKey="'VideoPause'" />
          </el-button>
          <el-button v-if="item.iscommonUpload" circle size="small">
            <icon :iconKey="'VideoPlay'" />
          </el-button>
          <el-button circle size="small">
            <icon :iconKey="'Delete'" />
          </el-button>
        </div>
      </div>
      <el-progress :percentage="item.percentage" :status="getStatus(item)" />
      <span style="color: --el-color-error-dark-2"> {{ item.errMeesges }}</span>
    </div>

    <template #footer>
      <div class="dialog-footer">
        <!-- <el-button @click="getAliOss">测试</el-button> -->
        <el-button @click="close">关闭</el-button>
        <el-button type="primary" @click="submit">开始上传</el-button>
      </div>
    </template>
  </el-dialog>
</template>
<script lang="ts" setup>
import type { OssFace } from "@/types/file";
import { ossGetInfo, saveServer } from "@/api/files";
import { ref } from "vue";
import icon from "@/components/icon/index";
import { useAppStoreWithOut } from "@/store/modules/app";
import type {
  UploadInstance,
  UploadProps,
  UploadRawFile,
  UploadUserFile,
} from "element-plus";
import { ElMessage } from "element-plus";
import oss from "ali-oss";
import dayjs from "dayjs";
const store = useAppStoreWithOut();
interface Props {
  viseble: boolean;
}
interface slefUploadUserFile extends UploadUserFile {
  iscommonUpload: boolean;
  errMeesges: string;
  url: string;
}
const checkpoints = {};
const emit = defineEmits(["update:viseble"]);
const props = defineProps<Props>();
const uploadRef = ref<UploadInstance>();
const FileList = ref<slefUploadUserFile[]>([]);
const ossInfo = ref<OssFace>();
const ossConfig = {
  partSize: 1024 * 1024, // 每个分片大小(byte)
  parallel: 3, // 同时上传的分片数
  path: dayjs().format("YYYYMMDD"),
};
const appOss = ref<oss>();
const getOssInfo = async () => {
  try {
    const res = await ossGetInfo<OssFace>();
    ossInfo.value = res;
    return res;
  } catch (error) {
    return Promise.reject("oss请求失败");
  }
};
const handelRequest = async (options: slefUploadUserFile) => {
  const file = options.raw;
  if (!appOss.value) {
    await getAliOss();
  }
  const eleFile = getFileInfo(file);
  try {
    if (file.size < ossConfig.partSize) {
      const res = await commonUpload(file);
      eleFile.url = res.url;
      eleFile.status = "success";
      eleFile.percentage = 100;
      eleFile.errMeesges = "";
      saveDataServer(eleFile);
    } else {
      await multipartUpload(file);
    }
  } catch (error) {
    eleFile.errMeesges = error || error.message;
    eleFile.status = "fail";
    eleFile.percentage = 0;
  }
  //   setFileInfo(eleFile)
};
const commonUpload = async (file: UploadRawFile) => {
  try {
    const slefPath = `${ossConfig.path}/${file.name}`;
    return await appOss.value.put(slefPath, file);
  } catch (error) {
    ElMessage.error(error.message);
    return Promise.reject("上传失败");
  }
};
const multipartUpload = async (file: UploadRawFile) => {
  try {
    const eleFile = getFileInfo(file);
    const slefPath = `${ossConfig.path}/${file.name}`;

    const res = (await appOss.value.multipartUpload(slefPath, file, {
      parallel: ossConfig.parallel,
      partSize: ossConfig.partSize,
      progress: async (e: number, checkpoint: any) => {
        eleFile.percentage = Math.floor(Number(e.toFixed(2)) * 100);
        await onMultipartUploadProgress(e, checkpoint);
      },
      //   checkpoint
    })) as any;
    console.log();
    eleFile.url = res.res.requestUrls[0];
    eleFile.status = "success";
    eleFile.percentage = 100;
    eleFile.errMeesges = "";
    saveDataServer(eleFile);
  } catch (error) {
    console.error(error.message || error);
    ElMessage.error(error.message || error);
    return Promise.reject(error.message || error);
  }
};
const resumeMultipartUpload = () => {
  Object.values(checkpoints).forEach(async (checkpoint: any) => {
    const { uploadId, file, name } = checkpoint;
    const eleFile = getFileInfo(file);
    const result: any = await appOss.value.multipartUpload(uploadId, file, {
      parallel: ossConfig.parallel,
      partSize: ossConfig.partSize,
      progress: async (e: number, checkpoint: any) => {
        eleFile.percentage = Math.floor(Number(e.toFixed(2)) * 100);
        await onMultipartUploadProgress(e, checkpoint);
      },
      checkpoint,
    });
    delete checkpoints[checkpoint.uploadId];
    eleFile.url = eleFile.url = result.res.requestUrls[0];
    eleFile.status = "success";
    eleFile.percentage = 100;
    eleFile.errMeesges = "";
    saveDataServer(eleFile);
  });
};
const onMultipartUploadProgress = async (e: number, checkpoint: any) => {
  console.log(e, checkpoint);
  try {
    if (!checkpoint) return;
    checkpoints[checkpoint.uploadId] = checkpoint;
    const timegap = 1;
    if (
      dayjs(ossInfo.value.credentials.Expiration)
        .subtract(timegap, "minute")
        .isBefore(dayjs())
    ) {
      console.log(
        `STS token will expire in ${timegap} minutes，uploading will pause and resume after getting new STS token`
      );
      await getAliOss();
      resumeMultipartUpload();
    }
  } catch (error) {
    console.error(error.message || error);
    ElMessage.error(error.message || error);
  }
};
const getFileInfo = (file: UploadRawFile) => {
  const eleFile = FileList.value.find((e) => e.uid === file.uid);
  if (eleFile) {
    return eleFile;
  } else {
    throw new Error("未找到文件信息");
  }
};
const setFileInfo = (file: slefUploadUserFile) => {
  if (FileList.value.length === 0) return;
  const ls: slefUploadUserFile[] = [];
  for (let index = 0; index < FileList.value.length; index++) {
    const element = FileList.value[index];
    if (element.uid === file.uid) {
      ls.push(file);
    } else {
      ls.push(element);
    }
  }
  FileList.value = ls;
  console.log(FileList.value);
};
const close = () => {
  emit("update:viseble", false);
};
const beforeAvatarUpload: UploadProps["beforeUpload"] = (rawFile) => {
  if (rawFile.size / 1024 / 1024 > 10) {
    ElMessage.error("Avatar picture size can not exceed 10MB!");
    return false;
  }
  return true;
};
const submit = () => {
  //   uploadRef.value?.submit();
  FileList.value.map((e) => {
    handelRequest(e);
  });
};
const getAliOss = async () => {
  const ossInfo = await getOssInfo();
  const slefOss = new oss({
    accessKeyId: ossInfo.credentials.AccessKeyId,
    accessKeySecret: ossInfo.credentials.AccessKeySecret,
    region: ossInfo.address,
    stsToken: ossInfo.credentials.securityToken,
    bucket: ossInfo.bucket,
    endpoint: ossInfo.point,
  });
  appOss.value = slefOss;
};
const getStatus = (e: slefUploadUserFile) => {
  //  "ready" | "uploading" | "success" | "fail"
  if (e.status === "success") {
    return "success";
  } else if (e.status === "fail") {
    return "exception";
  } else {
    return "";
  }
};
const saveDataServer = (data: slefUploadUserFile) => {
  const parmas = {
    path: data.url,
    name: data.name,
    size: data.size,
    suffix: data.name.split(".").pop(),
    userId: store.getSlefUsetInfo.uuid,
  };
  saveServer(parmas);
};
</script>
<style lang="scss" scoped>
.file-progress {
  padding: 10px;
}
.file-progress:hover {
  background-color: #eee;
}
</style>
